package net.sf.rose.jdbc.service;

import java.sql.Connection;
import java.sql.SQLException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;

import javax.sql.DataSource;

import org.apache.log4j.Logger;

import net.sf.rose.boot.BootStrap;
import net.sf.rose.boot.bean.DataTypeBean;
import net.sf.rose.boot.bean.DatabaseBean;
import net.sf.rose.jdbc.bean.DataAuthorBean;
import net.sf.rose.jdbc.bean.DbUserBean;
import net.sf.rose.jdbc.bean.TableBean;
import net.sf.rose.jdbc.bean.annotation.Table;
import net.sf.rose.jdbc.database.Jdbc;
import net.sf.rose.jdbc.database.Mysql;
import net.sf.rose.jdbc.database.Oracle;
import net.sf.rose.jdbc.database.SqlServer;
import net.sf.rose.jdbc.database.pgsql;
import net.sf.rose.util.BeanWrapper;
import net.sf.rose.util.ClassUtils;

/**
 * 类说明：
 * 
 * @author fengjian
 * @since 2012/06/23
 */
public class Service {

	private static Logger LOG = Logger.getLogger(Service.class);

	/** 默认数据库 */
	private static DatabaseBean defaultDatabase = null;

	/** 默认数据库连接池 */
	private static DataSource dataSource = null;

	/** 多数据库用数据库连接池 */
	private static Map<String, DataSource> dataSourceMap = new HashMap<>();

	/** 数据库名称对应的数据库类型 */
	private static Map<String, String> dataSourceType = new HashMap<>();

	/** 数据库表定义缓存 */
	private static Map<String, TableBean> ct = null;

	/** 数据库定义元数据 */
	private static MetaService meta = null;

	/** 本线程数据库连接 */
	private static final ThreadLocal<Service> serviceSession = new ThreadLocal<>();

	/** 本线程当前用户 */
	private static final ThreadLocal<DbUserBean> currentUserSession = new ThreadLocal<>();

	/** 页面数据权限缓存 */
	private static Map<String, DataAuthorBean> pagePower = new HashMap<>();

	/**
	 * 获取当前数据库连接，没有则创建
	 * 
	 * @return
	 */
	public static Service getService() {
		Service s = (Service) serviceSession.get();
		if (s == null) {
			s = new Service();
			serviceSession.set(s); // 将新开的Session保存到线程局部变量中
		}
		s.setUser(currentUserSession.get());
		return s;
	}

	/**
	 * 关闭当前数据库连接
	 */
	public static void closeService() {
		Service s = (Service) serviceSession.get();
		serviceSession.remove();
		if (s != null) {
			s.close();
		}
		currentUserSession.remove();
	}

	/**
	 * 关闭当前数据库连接，没有则放弃
	 */
	public static void commitService() {
		Service s = (Service) serviceSession.get();
		if (s != null) {
			s.commit();
		}
	}

	/**
	 * 关闭当前数据库连接，没有则放弃
	 */
	public static void commitAndCloseService() {
		Service s = (Service) serviceSession.get();
		serviceSession.remove();
		if (s != null) {
			s.commitAndClose();
		}
		currentUserSession.remove();
	}

	/**
	 * 回滚并关闭当前数据库连接
	 */
	public static void rollbackAndCloseService() {
		Service s = (Service) serviceSession.get();
		serviceSession.remove();
		if (s != null) {
			s.rollback();
			s.close();
		}
		currentUserSession.remove();
	}

	/**
	 * 获取当前用户
	 * 
	 * @return
	 */
	public static DbUserBean getCurrentUser() {
		return currentUserSession.get();
	}

	/**
	 * 设置当前用户
	 */
	public static void setCurrentUser(DbUserBean user) {
		currentUserSession.set(user);
	}

	/**
	 * 判断是否使用了数据库连接
	 * 
	 * @return 返回是否配置了数据库连接
	 */
	public static boolean isUsedDatabase() {
		return dataSourceMap.size() > 0;
	}

	/**
	 * 添加数据库表名称与数据库类型的对应关系
	 */
	public static void addDataSourceType(String fileName, String type) {
		Service.dataSourceType.put(fileName, type);
	}

	/**
	 * 设置数据库表定义缓存
	 * 
	 * @param ct 数据库表定义缓存
	 */
	public static void setTableCache(Map<String, TableBean> ct) {
		Service.ct = ct;
	}

	/**
	 * 获取数据库表定义缓存
	 * 
	 * @return ct 数据库表定义缓存
	 */
	public static Map<String, TableBean> getTableCache() {
		return Service.ct;
	}

	/**
	 * 添加数据库连接池
	 * 
	 * @param databaseName 数据库名称
	 * @param dataSource   数据库连接池
	 */
	public static void addDataSource(String databaseName, DataSource dataSource) {
		Service.dataSourceMap.put(databaseName, dataSource);
	}

	/**
	 * 设置默认操作的数据库
	 * 
	 * @param databaseName 数据库名称
	 */
	public static void setDefaultDatabaseName(String databaseName) {
		Service.defaultDatabase = BootStrap.getBootBean().getDatabaseBean(databaseName);
		Service.dataSource = dataSourceMap.get(databaseName);
	}

	/**
	 * 返回数据库
	 * 
	 * @return 数据库
	 */
	public static DatabaseBean getDefaultDatabase() {
		return Service.defaultDatabase;
	}

	/**
	 * 返回数据库名称
	 * 
	 * @return 数据库名称
	 */
	public static String getDefaultDatabaseName() {
		return Service.defaultDatabase.getFileName();
	}

	/**
	 * 返回数据库类型字符串
	 * 
	 * @param databaseName 数据库名称
	 * @return 数据库类型字符串
	 */
	public static String getDataSourceType(String databaseName) {
		return dataSourceType.get(databaseName);
	}

	/**
	 * 设置数据库定义元数据
	 * 
	 * @param meta 数据库定义元数据
	 */
	public static void setMeta(MetaService meta) {
		Service.meta = meta;
	}

	/**
	 * 获取数据库定义元数据
	 * 
	 * @return 数据库定义元数据
	 */
	public static MetaService getMeta() {
		return meta;
	}

	/**
	 * 根据数据库表或视图名称查找对应数据库表的元数据
	 * 
	 * @param tableName - 数据库表或视图名称
	 * @return 数据库表定义元数据类
	 */
	public static TableBean getTableBean(String tableName) {
		return ct.get(tableName);
	}

	/**
	 * 根据数据库表或视图名称查找对应数据库表的元数据
	 * 
	 * @param clazz - 数据库表或视图映射类
	 * @return 数据库表定义元数据类
	 */
	public static TableBean getTableBean(Class<?> clazz) {
		Table table = clazz.getAnnotation(Table.class);
		return Service.getTableBean(table.name());
	}

	/**
	 * 功能：获取数据库表定义列表
	 * 
	 * @return 数据库表定义列表
	 */
	public static List<TableBean> getTableList() {
		List<TableBean> list = new ArrayList<>();
		for (TableBean table : ct.values()) {
			if (!table.isView()) {
				list.add(table);
			}
		}
		Collections.sort(list);
		return list;
	}

	/**
	 * 功能：获取数据库视图定义列表
	 * 
	 * @return 数据库视图定义列表
	 */
	public static List<TableBean> getViewList() {
		List<TableBean> list = new ArrayList<>();
		for (TableBean table : ct.values()) {
			if (table.isView()) {
				list.add(table);
			}
		}
		Collections.sort(list);
		return list;
	}

	/**
	 * 功能：获取数据库所有表和视图定义列表
	 * 
	 * @return 数据库所有表和视图定义列表
	 */
	public static List<TableBean> getAllTableList() {
		List<TableBean> list = new ArrayList<>();
		for (TableBean table : ct.values()) {
			list.add(table);
		}
		return list;
	}

	/**
	 * 获取数据库字段类型定义(键是名称)
	 * 
	 * @return 数据库字段类型定义
	 */
	public Map<String, DataTypeBean> getDataType() {
		return BootStrap.getBootBean().getDatabaseBean(currentDatabaseFileName).getDataTypeMap();
	}

	/**
	 * 获取数据库字段类型定义(键是数据库类型)
	 * 
	 * @return 数据库字段类型定义
	 */
	public Map<String, DataTypeBean> getDataBaseTypeMap() {
		return BootStrap.getBootBean().getDatabaseBean(currentDatabaseFileName).getDataBaseTypeMap();
	}

	/**
	 * 获取某个数据表的主键名称
	 * 
	 * @param bean - 数据对象实体类
	 * @return 主键值
	 */
	public static String getTablePkName(Class<?> clazz) {
		Table table = clazz.getAnnotation(Table.class);
		TableBean tb = Service.getTableBean(table.name());
		return tb.getPkField().getPropertyName();
	}

	/**
	 * 获取某个数据表的主键值
	 * 
	 * @param bean - 数据对象实体类
	 * @return 主键值
	 */
	public static String getTablePkValue(Object bean) {
		Class<?> clazz = bean.getClass();
		Table table = clazz.getAnnotation(Table.class);
		TableBean tb = Service.getTableBean(table.name());
		String name = tb.getPkField().getPropertyName();
		BeanWrapper bw = new BeanWrapper(bean);
		Object str = bw.get(name);
		return str == null ? null : str.toString();
	}

	/**
	 * 设置页面数据权限缓存
	 * 
	 * @param map - 页面数据权限缓存
	 */
	public static void setPagePowerCache(Map<String, DataAuthorBean> map) {
		pagePower = map;
	}

	/**
	 * 根据某个页面编号获取页面数据权限缓存
	 * 
	 * @param pageID - 页面编号
	 * @return 页面数据权限缓存
	 */
	public static DataAuthorBean getPagePower(String menuID) {
		return pagePower.get(menuID);
	}

	/**
	 * 设置页面数据权限缓存
	 * 
	 * @param bean - 页面数据权限缓存
	 */
	public static void setPagePower(String menuID, DataAuthorBean bean) {
		pagePower.put(menuID, bean);
	}

	/** 数据库操作的执行者 */
	private DbUserBean user = null;

	/** 当前使用的数据库连接池 */
	private DataSource currentDataSource = null;

	/** 当前使用的数据库 */
	private DatabaseBean currentDatabase = null;

	/** 当前使用的数据库名称 */
	private String currentDatabaseFileName = null;

	/** 已使用的数据库连接 */
	private Map<String, Connection> connectionMap = new HashMap<>();

	/** 需要数据库提交 */
	private boolean needCommit = false;

	/** 当前操作的页面编号 */
	private String pageID = null;

	/** 是否打印日志 */
	private boolean recordLog = true;

	public Service() {
		super();
		this.currentDataSource = Service.dataSource;
		this.currentDatabase = Service.defaultDatabase;
		this.currentDatabaseFileName = Service.defaultDatabase.getFileName();
	}

	public Service(String databaseName) {
		super();
		this.setCurrentDatabaseName(databaseName);
	}

	/**
	 * 设置当前要操作的数据库连接池
	 * 
	 * @param databaseName - 数据库名称
	 */
	public void setCurrentDatabaseName(String databaseName) {
		this.currentDatabase = BootStrap.getBootBean().getDatabaseBean(databaseName);
		this.currentDataSource = Service.dataSourceMap.get(databaseName);
		this.currentDatabaseFileName = currentDatabase.getFileName();
	}

	/**
	 * 获取当前要操作的数据库名称
	 * 
	 * @return 当前要操作的数据库名称
	 */
	public String getCurrentDatabaseName() {
		return this.currentDatabaseFileName;
	}

	/**
	 * 设置数据库操作的执行者
	 * 
	 * @param user 数据库操作的执行者
	 */
	public void setUser(DbUserBean user) {
		this.user = user;
	}

	/**
	 * 返回数据库操作的执行者
	 * 
	 * @return 数据库操作的执行者
	 */
	public DbUserBean getUser() {
		return user;
	}

	/**
	 * 设置是否需要数据库提交
	 * 
	 * @param needCommit - 是否需要数据库提交
	 */
	public void setNeedCommit(boolean needCommit) {
		this.needCommit = needCommit;
	}

	/**
	 * 返回数据库操作是否需要提交
	 * 
	 * @return 数据库操作是否需要提交
	 */
	public boolean isNeedCommit() {
		return needCommit;
	}

	/**
	 * 返回当前数据库类型
	 * 
	 * @return 当前数据库类型
	 */
	public String getDatabaseType() {
		return dataSourceType.get(this.currentDatabaseFileName);
	}

	/**
	 * 返回SQL语句
	 * 
	 * @param key - SQL语句键值
	 * @return SQL语句
	 */
	public String getSql(String key) {
		return this.currentDatabase.getResourceSql().get(key);
	}

	/**
	 * 返回SQL语句
	 * 
	 * @param key       - SQL语句键值
	 * @param arguments - 参数
	 * @return SQL语句
	 */
	public String getSql(String key, Object... arguments) {
		try {
			String sql = getSql(key);
			return sql == null ? null : new MessageFormat(sql).format(arguments);
		} catch (MissingResourceException e) {
			throw ClassUtils.getException(e);
		}
	}

	/**
	 * 返回当前操作的页面编号
	 * 
	 * @return 当前操作的页面编号
	 */
	public String getPageID() {
		return this.pageID;
	}

	/**
	 * 设置当前操作的页面编号
	 * 
	 * @param pageID - 页面编号
	 */
	public void setPageID(String pageID) {
		this.pageID = pageID;
	}

	/**
	 * 根据当前操作的页面编号返回角色数据权限
	 * 
	 * @return 角色数据权限
	 */
	public DataAuthorBean getPagePower() {
		return this.pageID == null ? null : pagePower.get(this.pageID);
	}

	/**
	 * 返回数据库连接
	 * 
	 * @return 数据库连接
	 */
	public Connection getConnection() {
		Connection connection = connectionMap.get(this.currentDatabaseFileName);
		if (connection != null) {
			return connection;
		}
		try {
			connection = currentDataSource.getConnection();
			connection.setAutoCommit(false);
			connectionMap.put(this.currentDatabaseFileName, connection);
			return connection;
		} catch (SQLException e) {
			throw ClassUtils.getException("数据库连接创建失败！", e);
		}
	}

	/**
	 * 提交数据至数据库
	 */
	public void commit() {
		if (connectionMap.size() == 0) {
			return;
		}
		try {
			for (Connection con : connectionMap.values()) {
				if (con != null) {
					con.commit();
				}
			}
		} catch (SQLException e) {
			throw ClassUtils.getException("数据更新提交至数据库失败！", e);
		}
		if (this.needCommit) {
			this.needCommit = false;
		}
	}

	/**
	 * 提交数据至数据库
	 */
	public void rollback() {
		if (connectionMap.size() == 0) {
			return;
		}
		try {
			for (Connection con : connectionMap.values()) {
				if (con != null) {
					con.rollback();
				}
			}
		} catch (SQLException e) {
			LOG.error("数据回滚异常！\n" + e.getMessage(), e);
		}
	}

	/**
	 * 关闭数据库连接
	 */
	public void close() {
		if (connectionMap.size() == 0) {
			return;
		}
		try {
			for (Connection con : connectionMap.values()) {
				if (con != null && !con.isClosed()) {
					con.close();
					if (this.isRecordLog()) {
						LOG.info(Thread.currentThread().getId() + "数据库连接关闭成功...");
					}
				}
			}
		} catch (SQLException e) {
			LOG.error("数据库连接关闭失败！\n" + e.getMessage(), e);
		}
		this.connectionMap.clear();
		this.needCommit = false;
	}

	/**
	 * 提交并关闭数据连接
	 */
	public void commitAndClose() {
		commit();
		close();
	}

	/**
	 * 返回指定的数据库操作实现类
	 * 
	 * @return 数据库操作实现类
	 */
	public Jdbc getJdbcSqlCreator() {
		Jdbc jdbc = null;
		String type = dataSourceType.get(this.currentDatabaseFileName);
		if (type.equals("mysql")) {
			jdbc = new Mysql();
		} else if (type.equals("oracle")) {
			jdbc = new Oracle();
		} else if (type.equals("sqlserver")) {
			jdbc = new SqlServer();
		} else if (type.equals("pgsql")) {
			jdbc = new pgsql();
		}
		return jdbc;
	}

	public void setRecordLog(boolean flag) {
		this.recordLog = flag;
	}

	public boolean isRecordLog() {
		return this.recordLog;
	}

}
